Skip to content

feat(fw): implement DDI HkdfDerive + KbkdfCounterHmacDerive handlers#430

Merged
jaygmsft merged 3 commits into
mainfrom
user/jayg/hkdf_kbkdf
Jun 8, 2026
Merged

feat(fw): implement DDI HkdfDerive + KbkdfCounterHmacDerive handlers#430
jaygmsft merged 3 commits into
mainfrom
user/jayg/hkdf_kbkdf

Conversation

@jaygmsft

@jaygmsft jaygmsft commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Wire up the previously-unimplemented HkdfDerive (1075) and KbkdfCounterHmacDerive (1076) DDI ops in the firmware application layer, deriving key material from an existing ECDH shared secret and storing the result in the partition vault.

Behavior mirrors the reference firmware (mcr-hsm) with one deliberate divergence: every HMAC output is stored as the variable-length HMAC vault kind (VarLenHmacSha256/384/512) rather than the deprecated fixed-length _HmacSha* kinds.

  • Input key must be an ECDH shared secret (Secret256/384/521) with the derive permission; any other kind is rejected with InvalidKeyType.
  • Output key_type dispatch:
    • Aes128/192/256 -> AES vault kinds (encrypt/decrypt usage)
    • HmacSha256/384/512 -> VarLenHmacSha256/384/512 (sign/verify)
    • VarHmac256/384/512 -> VarLenHmacSha256/384/512 (sign/verify),
      key_length required (else InvalidKeyType) and range-checked
      (256:32-64, 384:48-128, 512:64-128; else InvalidKeyLength)
    • bulk AES / other -> InvalidKeyType (out of scope)
  • HKDF runs RFC 5869 Extract-then-Expand; KBKDF runs the SP 800-108
    counter-mode HMAC PRF. Absent salt/info/label/context use a
    zero-length DmaBuf.
  • masked_key is an empty placeholder pending the UnmaskKey handler,
    consistent with the other key-creating handlers.

New: kdf.rs (shared input/output resolution), hkdf_derive.rs, kbkdf_derive.rs, key_attrs::for_var_hmac; mod.rs dispatch wiring. Tests: hkdf_smoke.rs / kbkdf_smoke.rs (AES round-trip + fixed-HMAC derive on both backends; var-HMAC derive + length validation gated to emu, since the sim has no variable-length HMAC kind).

Validation:

  • emu hkdf_smoke 5/5, kbkdf_smoke 5/5; mock hkdf_smoke 2/2, kbkdf_smoke 2/2.
  • emu secret_hkdf_derive / secret_kbkdf_derive: all in-scope tests pass (remaining failures depend on the unimplemented Hmac/OpenKey ops and bulk AES, which were already failing as UnsupportedCmd).
  • emu smoke suite 37/37; mock secret_hkdf_derive 29/29 (no regression).
  • cargo xtask clippy clean; clippy --tests clean under emu and mock; fmt and copyright clean.

Wire up the previously-unimplemented HkdfDerive (1075) and
KbkdfCounterHmacDerive (1076) DDI ops in the firmware application
layer, deriving key material from an existing ECDH shared secret and
storing the result in the partition vault.

Behavior mirrors the reference firmware (mcr-hsm) with one deliberate
divergence: every HMAC output is stored as the variable-length HMAC
vault kind (VarLenHmacSha256/384/512) rather than the deprecated
fixed-length _HmacSha* kinds.

- Input key must be an ECDH shared secret (Secret256/384/521) with the
  `derive` permission; any other kind is rejected with InvalidKeyType.
- Output key_type dispatch:
  - Aes128/192/256        -> AES vault kinds (encrypt/decrypt usage)
  - HmacSha256/384/512    -> VarLenHmacSha256/384/512 (sign/verify)
  - VarHmac256/384/512    -> VarLenHmacSha256/384/512 (sign/verify),
    key_length required (else InvalidKeyType) and range-checked
    (256:32-64, 384:48-128, 512:64-128; else InvalidKeyLength)
  - bulk AES / other      -> InvalidKeyType (out of scope)
- HKDF runs RFC 5869 Extract-then-Expand; KBKDF runs the SP 800-108
  counter-mode HMAC PRF. Absent salt/info/label/context use a
  zero-length DmaBuf.
- masked_key is an empty placeholder pending the UnmaskKey handler,
  consistent with the other key-creating handlers.

New: kdf.rs (shared input/output resolution), hkdf_derive.rs,
kbkdf_derive.rs, key_attrs::for_var_hmac; mod.rs dispatch wiring.
Tests: hkdf_smoke.rs / kbkdf_smoke.rs (AES round-trip + fixed-HMAC
derive on both backends; var-HMAC derive + length validation gated to
emu, since the sim has no variable-length HMAC kind).

Validation:
- emu hkdf_smoke 5/5, kbkdf_smoke 5/5; mock hkdf_smoke 2/2,
  kbkdf_smoke 2/2.
- emu secret_hkdf_derive / secret_kbkdf_derive: all in-scope tests
  pass (remaining failures depend on the unimplemented Hmac/OpenKey
  ops and bulk AES, which were already failing as UnsupportedCmd).
- emu smoke suite 37/37; mock secret_hkdf_derive 29/29 (no regression).
- cargo xtask clippy clean; clippy --tests clean under emu and mock;
  fmt and copyright clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 6, 2026 01:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements the previously-unhandled DDI KDF operations in the firmware MBOR application layer: HkdfDerive and KbkdfCounterHmacDerive. It validates that the input key is an ECDH shared secret with derive permission, derives key material via HKDF (RFC 5869) or KBKDF counter-mode HMAC (SP 800-108), and stores the derived key in the partition vault (with HMAC outputs stored as VarLen HMAC vault kinds).

Changes:

  • Added MBOR dispatch wiring for DdiOp::HkdfDerive and DdiOp::KbkdfCounterHmacDerive, plus new handler modules.
  • Introduced shared KDF target-resolution / input-kind validation in kdf.rs, and added key_attrs::for_var_hmac for derived HMAC outputs.
  • Added integration smoke tests for HKDF/KBKDF derivation and wired them into the MBOR types test suite.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
fw/core/lib/src/ddi/mbor/mod.rs Wires new DDI ops into MBOR dispatch and exports handler modules.
fw/core/lib/src/ddi/mbor/key_attrs.rs Adds derived VarLen-HMAC attribute builder (for_var_hmac).
fw/core/lib/src/ddi/mbor/kdf.rs Shared validation + output key-type/length → vault kind mapping logic for both KDF ops.
fw/core/lib/src/ddi/mbor/hkdf_derive.rs Implements HKDF derive handler (extract + expand) and vault persistence.
fw/core/lib/src/ddi/mbor/kbkdf_derive.rs Implements SP 800-108 counter-mode HMAC derive handler and vault persistence.
ddi/mbor/types/tests/integration/hkdf_smoke.rs Adds HKDF smoke tests for AES and HMAC outputs.
ddi/mbor/types/tests/integration/kbkdf_smoke.rs Adds KBKDF smoke tests for AES and HMAC outputs.
ddi/mbor/types/tests/azihsm_ddi_tests.rs Registers the new smoke-test modules.

Comment thread ddi/mbor/types/tests/integration/hkdf_smoke.rs
Comment thread ddi/mbor/types/tests/integration/hkdf_smoke.rs
Comment thread ddi/mbor/types/tests/integration/hkdf_smoke.rs
Comment thread ddi/mbor/types/tests/integration/kbkdf_smoke.rs
Comment thread ddi/mbor/types/tests/integration/kbkdf_smoke.rs
Comment thread ddi/mbor/types/tests/integration/kbkdf_smoke.rs
@jaygmsft jaygmsft enabled auto-merge (squash) June 6, 2026 01:16
vsonims
vsonims previously approved these changes Jun 8, 2026
Comment thread fw/core/lib/src/ddi/mbor/kbkdf_derive.rs Outdated
jaygmsft and others added 2 commits June 8, 2026 19:57
Previously the optional HKDF (salt/info) and SP 800-108 KBKDF
(label/context) inputs were passed as a non-optional `&DmaBuf`, where
a zero-length buffer signaled "absent". Handlers had to materialize an
empty `DmaBuf` via `split_at_mut(0)` and branch on
`match body.field.as_deref() { Some(x) => x, None => empty }`.

Make these params `Option<&DmaBuf>` through the HsmKdf trait, the std
PAL impl, the std KDF driver, and all call sites, so handlers pass
`body.field.as_deref()` directly. The std driver keeps the
empty-as-absent normalization (`owned_nonempty`) so the bytes handed to
OpenSSL stay byte-identical to the previous flow.

In hkdf_derive, `out` and `prk` are now two independent dma_alloc calls
(each 4-byte aligned) rather than carving an empty placeholder off a
shared buffer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jaygmsft jaygmsft merged commit a13cd21 into main Jun 8, 2026
6 checks passed
@jaygmsft jaygmsft deleted the user/jayg/hkdf_kbkdf branch June 8, 2026 23:22
vsonims added a commit that referenced this pull request Jun 9, 2026
* feat(fw): implement DDI DeleteKey handler (#431)

Wire up the previously-unimplemented DeleteKey (1014) DDI op in the
firmware application layer: within an open session, delete a
vault-resident key by id and return an empty success response.

- Internal device keys (anything carrying the `internal` attribute —
  the partition unwrapping key, session and credential keys) cannot be
  destroyed by the host and are rejected with
  CannotDeleteInternalKeys, matching the reference firmware.
- An unknown key_id is rejected with KeyNotFound.
- Session validation is framework-level (DeleteKey -> InSession), so
  a missing/mismatched session returns FileHandleSessionIdDoesNotMatch.

The handler is synchronous: the `internal`-attribute check and the
vault deletion have no yield point between them, so no partition_lock
is needed.

New: delete_key.rs handler; mod.rs dispatch wiring; delete_key_smoke.rs
smoke tests (delete + reuse, unknown-key, and double-delete idempotency
using an app AES key, so they run on both backends).

Validation:
- emu delete_key_smoke 3/3; mock 3/3.
- mock delete_key integration 8/8 (includes CannotDeleteInternalKeys
  for the unwrapping key). On emu the core delete_key integration
  tests pass (6/8); the unwrapping-key and bulk-import cases are
  setup-blocked by GetUnwrappingKey / RsaUnwrap, which are not yet
  implemented on main (a pre-existing limitation, not this handler) —
  the internal-key path is exercised on mock instead.
- emu smoke 33/33 (no regression).
- cargo xtask clippy clean; clippy --tests clean under emu and mock;
  fmt and copyright clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(fw): implement DDI HkdfDerive + KbkdfCounterHmacDerive handlers (#430)

Wire up the previously-unimplemented HkdfDerive (1075) and
KbkdfCounterHmacDerive (1076) DDI ops in the firmware application
layer, deriving key material from an existing ECDH shared secret and
storing the result in the partition vault.

Behavior mirrors the reference firmware (mcr-hsm) with one deliberate
divergence: every HMAC output is stored as the variable-length HMAC
vault kind (VarLenHmacSha256/384/512) rather than the deprecated
fixed-length _HmacSha* kinds.

- Input key must be an ECDH shared secret (Secret256/384/521) with the
  `derive` permission; any other kind is rejected with InvalidKeyType.
- Output key_type dispatch:
  - Aes128/192/256        -> AES vault kinds (encrypt/decrypt usage)
  - HmacSha256/384/512    -> VarLenHmacSha256/384/512 (sign/verify)
  - VarHmac256/384/512    -> VarLenHmacSha256/384/512 (sign/verify),
    key_length required (else InvalidKeyType) and range-checked
    (256:32-64, 384:48-128, 512:64-128; else InvalidKeyLength)
  - bulk AES / other      -> InvalidKeyType (out of scope)
- HKDF runs RFC 5869 Extract-then-Expand; KBKDF runs the SP 800-108
  counter-mode HMAC PRF. Absent salt/info/label/context use a
  zero-length DmaBuf.
- masked_key is an empty placeholder pending the UnmaskKey handler,
  consistent with the other key-creating handlers.

New: kdf.rs (shared input/output resolution), hkdf_derive.rs,
kbkdf_derive.rs, key_attrs::for_var_hmac; mod.rs dispatch wiring.
Tests: hkdf_smoke.rs / kbkdf_smoke.rs (AES round-trip + fixed-HMAC
derive on both backends; var-HMAC derive + length validation gated to
emu, since the sim has no variable-length HMAC kind).

Validation:
- emu hkdf_smoke 5/5, kbkdf_smoke 5/5; mock hkdf_smoke 2/2,
  kbkdf_smoke 2/2.
- emu secret_hkdf_derive / secret_kbkdf_derive: all in-scope tests
  pass (remaining failures depend on the unimplemented Hmac/OpenKey
  ops and bulk AES, which were already failing as UnsupportedCmd).
- emu smoke suite 37/37; mock secret_hkdf_derive 29/29 (no regression).
- cargo xtask clippy clean; clippy --tests clean under emu and mock;
  fmt and copyright clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(fw): implement DDI Hmac handler (#445)

Wire up the previously-unimplemented Hmac (1077) DDI op in the
firmware application layer: compute an HMAC tag over a host-supplied
message using a vault-resident HMAC key.

- The key may be any HMAC kind — fixed-length _HmacSha* or
  variable-length VarLenHmacSha* (the latter is what HKDF / KBKDF
  derive in this firmware). The key's hash variant selects the MAC
  algorithm and tag length (SHA-256/384/512 -> 32/48/64 bytes),
  matching the reference firmware's Hmac handler.
- An unknown key_id surfaces as KeyNotFound; a non-HMAC key as
  InvalidKeyType.
- Generating a MAC is a PKCS#11 C_Sign operation, so the key must
  carry CKA_SIGN; a derive-only HMAC key is rejected with
  InvalidPermissions.

Deriving an HMAC key via KBKDF with no label/context (as the hmac
integration tests do) previously failed: azihsm_crypto's KbkdfAlgo
rejected deriving with both Label and Context absent. SP 800-108
permits empty Label/Context (and the sim/reference allow it), so relax
KbkdfAlgo to permit both-absent — the PRF input reduces to the counter
(and optional length field), leaving all other cases byte-identical.

New: hmac.rs handler, from_pal::hmac_hash; mod.rs dispatch wiring;
hmac_smoke.rs smoke tests (fixed-HMAC MAC + unknown-key on both
backends; var-HMAC MAC and the derive-only sign-permission rejection
gated to emu, since the sim has no var-len HMAC kind and only permits
SignVerify on HMAC keys).

Validation:
- emu integration::hmac:: 17/17 and hmac_smoke 4/4; mock 10/10 and
  hmac_smoke 2/2.
- The Hmac op + KBKDF change advance secret_hkdf_derive and
  secret_kbkdf_derive from 15/29 to 19/29 each on emu (the *_aes*_sha*
  HMAC-roundtrip tests now pass; remaining failures are bulk AES and
  the unimplemented OpenKey op).
- azihsm_crypto 476/476; std PAL unit 128/128; emu smoke 50/50;
  hkdf_smoke / kbkdf_smoke pass on emu and mock (no regression).
- cargo xtask clippy clean; clippy --tests clean under emu and mock;
  fmt and copyright clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Jayant Gandhi <jayg@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants